---
title: "類和結構體 - 物件導向程式設計、屬性和方法"
description: "學習 Swift 類和結構體：物件導向程式設計基礎、屬性、方法、建構函式，與 JavaScript 對比"
---

# 類和結構體：物件導向程式設計、屬性和方法

在本模組中，我們將探索 Swift 中的物件導向程式設計（OOP），並與 JavaScript 的類和原型系統進行對比。Swift 提供了類（參考類型）和結構體（值類型），每種都有其特定的用例和特性。

## 目錄
- [介紹：類與結構體](#介紹類與結構體)
- [宣告類和結構體](#宣告類和結構體)
- [屬性](#屬性)
- [方法](#方法)
- [初始化器（建構函式）](#初始化器建構函式)
- [繼承和多態](#繼承和多態)
- [值類型與參考類型](#值類型與參考類型)
- [協議（介面）](#協議介面)
- [進階特性](#進階特性)
- [練習題](#練習題)
- [關鍵要點](#關鍵要點)

## 介紹：類與結構體

- **類**：參考類型，支援繼承、解構器、身份比較、參考語義。
- **結構體**：值類型，不支援繼承，賦值時複製，值語義，輕量級，適合簡單資料模型。

| 特性           | JavaScript 類 | Swift 類 | Swift 結構體 |
|----------------|---------------|----------|--------------|
| 參考類型       | 是            | 是       | 否           |
| 值類型         | 否            | 否       | 是           |
| 繼承           | 是            | 是       | 否           |
| 解構器         | 否            | 是       | 否           |
| 可變性         | 動態          | 受控     | 受控         |
| 協議           | 否（介面）    | 是       | 是           |
| 使用場景       | 所有 OOP      | OOP      | 資料模型、輕量級類型 |

## 宣告類和結構體

<UniversalEditor title="類和結構體宣告對比" compare={true}>
```javascript !! js
// JavaScript 類宣告
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    greet() {
        console.log(`你好，我的名字是 ${this.name}，我 ${this.age} 歲。`);
    }
}

const alice = new Person("Alice", 30);
alice.greet(); // 你好，我的名字是 Alice，我 30 歲。

// JavaScript 物件字面量（類似結構體）
const point = { x: 3, y: 4 };
console.log(point.x, point.y);
```

```swift !! swift
// Swift 類宣告
class Person {
    var name: String
    var age: Int

    // 初始化器
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    // 方法
    func greet() {
        print("你好，我的名字是 \(name)，我 \(age) 歲。")
    }
}

let alice = Person(name: "Alice", age: 30)
alice.greet() // 你好，我的名字是 Alice，我 30 歲。

// Swift 結構體宣告
struct Point {
    var x: Int
    var y: Int
}

let point = Point(x: 3, y: 4)
print(point.x, point.y)
```
</UniversalEditor>

## 屬性

Swift 支援儲存屬性、計算屬性和屬性觀察器。JavaScript 的屬性總是動態的。

<UniversalEditor title="屬性對比" compare={true}>
```javascript !! js
// JavaScript 屬性
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    // 計算屬性（getter）
    get area() {
        return this.width * this.height;
    }

    // 屬性觀察器（手動實現）
    set width(value) {
        this._width = value;
        console.log("寬度改為", value);
    }
    get width() {
        return this._width;
    }
}

const rect = new Rectangle(10, 5);
console.log(rect.area); // 50
rect.width = 20; // 寬度改為 20
```

```swift !! swift
// Swift 屬性
struct Rectangle {
    var width: Double {
        didSet {
            print("寬度改為", width)
        }
    }
    var height: Double

    // 計算屬性
    var area: Double {
        return width * height
    }
}

var rect = Rectangle(width: 10, height: 5)
print(rect.area) // 50
rect.width = 20 // 寬度改為 20
```
</UniversalEditor>

## 方法

JavaScript 和 Swift 都支援實例方法。Swift 還支援靜態/類方法和結構體的可變方法。

<UniversalEditor title="方法對比" compare={true}>
```javascript !! js
// JavaScript 實例和靜態方法
class Counter {
    constructor() {
        this.count = 0;
    }
    increment() {
        this.count++;
    }
    static description() {
        return "一個簡單的計數器";
    }
}

const counter = new Counter();
counter.increment();
console.log(counter.count); // 1
console.log(Counter.description()); // 一個簡單的計數器
```

```swift !! swift
// Swift 實例、靜態和可變方法
struct Counter {
    var count = 0
    // 可變方法（用於結構體）
    mutating func increment() {
        count += 1
    }
    // 靜態方法
    static func description() -> String {
        return "一個簡單的計數器"
    }
}

var counter = Counter()
counter.increment()
print(counter.count) // 1
print(Counter.description()) // 一個簡單的計數器
```
</UniversalEditor>

## 初始化器（建構函式）

Swift 使用 `init` 作為初始化器。結構體預設獲得成員初始化器。

<UniversalEditor title="初始化器對比" compare={true}>
```javascript !! js
// JavaScript 建構函式
class User {
    constructor(username, email) {
        this.username = username;
        this.email = email;
    }
}
const user = new User("bob", "bob@example.com");

// 物件字面量
const config = { host: "localhost", port: 8080 };
```

```swift !! swift
// Swift 類初始化器
class User {
    var username: String
    var email: String
    init(username: String, email: String) {
        self.username = username
        self.email = email
    }
}
let user = User(username: "bob", email: "bob@example.com")

// Swift 結構體成員初始化器
struct Config {
    var host: String
    var port: Int
}
let config = Config(host: "localhost", port: 8080)
```
</UniversalEditor>

## 繼承和多態

Swift 類支援單繼承，而 JavaScript 類也支援單繼承，但可以使用混入和組合。

### 基本繼承

<UniversalEditor title="繼承對比" compare={true}>
```javascript !! js
// JavaScript 繼承
class Animal {
    constructor(name) {
        this.name = name;
    }
    speak() {
        console.log(`${this.name} 發出聲音`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);
        this.breed = breed;
    }
    speak() {
        console.log(`${this.name} 汪汪叫`);
    }
}

const dog = new Dog("Buddy", "金毛");
dog.speak(); // Buddy 汪汪叫
```

```swift !! swift
// Swift 繼承
class Animal {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func speak() {
        print("\(name) 發出聲音")
    }
}

class Dog: Animal {
    var breed: String
    
    init(name: String, breed: String) {
        self.breed = breed
        super.init(name: name)
    }
    
    override func speak() {
        print("\(name) 汪汪叫")
    }
}

let dog = Dog(name: "Buddy", breed: "金毛")
dog.speak() // Buddy 汪汪叫
```
</UniversalEditor>

### 多態

<UniversalEditor title="多態對比" compare={true}>
```javascript !! js
// JavaScript 多態
class Cat extends Animal {
    speak() {
        console.log(`${this.name} 喵喵叫`);
    }
}

function makeAnimalSpeak(animal) {
    animal.speak();
}

const animals = [
    new Dog("Buddy", "金毛"),
    new Cat("Whiskers")
];

animals.forEach(makeAnimalSpeak);
// Buddy 汪汪叫
// Whiskers 喵喵叫
```

```swift !! swift
// Swift 多態
class Cat: Animal {
    override func speak() {
        print("\(name) 喵喵叫")
    }
}

func makeAnimalSpeak(_ animal: Animal) {
    animal.speak()
}

let animals: [Animal] = [
    Dog(name: "Buddy", breed: "金毛"),
    Cat(name: "Whiskers")
]

for animal in animals {
    makeAnimalSpeak(animal)
}
// Buddy 汪汪叫
// Whiskers 喵喵叫
```
</UniversalEditor>

## 值類型與參考類型

這是 Swift 和 JavaScript 之間的根本差異。

### 參考類型（類）

<UniversalEditor title="參考類型對比" compare={true}>
```javascript !! js
// JavaScript：所有物件都是參考類型
class Person {
    constructor(name) {
        this.name = name;
    }
}

const person1 = new Person("Alice");
const person2 = person1; // 參考複製
person2.name = "Bob";
console.log(person1.name); // "Bob"（共享參考）
console.log(person2.name); // "Bob"
console.log(person1 === person2); // true（相同參考）
```

```swift !! swift
// Swift：類是參考類型
class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

let person1 = Person(name: "Alice")
let person2 = person1 // 參考複製
person2.name = "Bob"
print(person1.name) // "Bob"（共享參考）
print(person2.name) // "Bob"
print(person1 === person2) // true（相同參考）
```
</UniversalEditor>

### 值類型（結構體）

<UniversalEditor title="值類型對比" compare={true}>
```javascript !! js
// JavaScript：沒有真正的值類型，但可以用 Object.assign 模擬
const point1 = { x: 1, y: 2 };
const point2 = Object.assign({}, point1); // 淺複製
point2.x = 3;
console.log(point1.x); // 1（原物件未改變）
console.log(point2.x); // 3
console.log(point1 === point2); // false（不同物件）
```

```swift !! swift
// Swift：結構體是值類型
struct Point {
    var x: Int
    var y: Int
}

var point1 = Point(x: 1, y: 2)
var point2 = point1 // 值複製
point2.x = 3
print(point1.x) // 1（原物件未改變）
print(point2.x) // 3
print(point1 == point2) // false（不同值）
```
</UniversalEditor>

## 協議（介面）

Swift 協議類似於 JavaScript 介面，但功能更強大。

<UniversalEditor title="協議對比" compare={true}>
```javascript !! js
// JavaScript：沒有內建介面，但可以使用鴨子類型
class Drawable {
    draw() {
        throw new Error("draw() 必須被實現");
    }
}

class Circle {
    constructor(radius) {
        this.radius = radius;
    }
    draw() {
        console.log(`繪製半徑為 ${this.radius} 的圓`);
    }
}

class Square {
    constructor(side) {
        this.side = side;
    }
    draw() {
        console.log(`繪製邊長為 ${this.side} 的正方形`);
    }
}

function drawShape(shape) {
    if (typeof shape.draw === 'function') {
        shape.draw();
    }
}

drawShape(new Circle(5));
drawShape(new Square(4));
```

```swift !! swift
// Swift：帶顯式遵循的協議
protocol Drawable {
    func draw()
}

class Circle: Drawable {
    var radius: Double
    
    init(radius: Double) {
        self.radius = radius
    }
    
    func draw() {
        print("繪製半徑為 \(radius) 的圓")
    }
}

struct Square: Drawable {
    var side: Double
    
    func draw() {
        print("繪製邊長為 \(side) 的正方形")
    }
}

func drawShape(_ shape: Drawable) {
    shape.draw()
}

drawShape(Circle(radius: 5))
drawShape(Square(side: 4))
```
</UniversalEditor>

## 進階特性

### 屬性包裝器和計算屬性

<UniversalEditor title="進階屬性" compare={true}>
```javascript !! js
// JavaScript：getter 和 setter
class BankAccount {
    constructor(balance) {
        this._balance = balance;
    }
    
    get balance() {
        return this._balance;
    }
    
    set balance(value) {
        if (value < 0) {
            throw new Error("餘額不能為負數");
        }
        this._balance = value;
    }
}

const account = new BankAccount(1000);
console.log(account.balance); // 1000
account.balance = 1500; // OK
// account.balance = -100; // 錯誤
```

```swift !! swift
// Swift：帶屬性觀察器的計算屬性
class BankAccount {
    private var _balance: Double = 0
    
    var balance: Double {
        get {
            return _balance
        }
        set {
            if newValue < 0 {
                fatalError("餘額不能為負數")
            }
            _balance = newValue
        }
    }
    
    // 屬性觀察器
    var accountNumber: String = "" {
        didSet {
            print("帳號從 \(oldValue) 改為 \(accountNumber)")
        }
    }
}

let account = BankAccount()
account.balance = 1000
print(account.balance) // 1000
account.balance = 1500 // OK
// account.balance = -100 // 致命錯誤
```
</UniversalEditor>

### 靜態和類方法

<UniversalEditor title="靜態和類方法" compare={true}>
```javascript !! js
// JavaScript：靜態方法
class MathUtils {
    static add(a, b) {
        return a + b;
    }
    
    static multiply(a, b) {
        return a * b;
    }
    
    static PI = 3.14159;
}

console.log(MathUtils.add(5, 3)); // 8
console.log(MathUtils.PI); // 3.14159
```

```swift !! swift
// Swift：靜態和類方法
struct MathUtils {
    static func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
    
    static func multiply(_ a: Int, _ b: Int) -> Int {
        return a * b
    }
    
    static let PI = 3.14159
}

class Calculator {
    class func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
    
    static func multiply(_ a: Int, _ b: Int) -> Int {
        return a * b
    }
}

print(MathUtils.add(5, 3)) // 8
print(MathUtils.PI) // 3.14159
print(Calculator.add(5, 3)) // 8
```
</UniversalEditor>

## 練習題

### 練習 1：建立學生管理系統

<UniversalEditor title="練習 1：學生管理" compare={true}>
```javascript !! js
// JavaScript 解答
class Student {
    constructor(name, id, grades = []) {
        this.name = name;
        this.id = id;
        this.grades = grades;
    }
    
    addGrade(grade) {
        if (grade >= 0 && grade <= 100) {
            this.grades.push(grade);
        }
    }
    
    get average() {
        if (this.grades.length === 0) return 0;
        return this.grades.reduce((sum, grade) => sum + grade, 0) / this.grades.length;
    }
    
    get letterGrade() {
        const avg = this.average;
        if (avg >= 90) return 'A';
        if (avg >= 80) return 'B';
        if (avg >= 70) return 'C';
        if (avg >= 60) return 'D';
        return 'F';
    }
}

const student = new Student("Alice", "12345");
student.addGrade(85);
student.addGrade(92);
student.addGrade(78);
console.log(`${student.name}: ${student.average.toFixed(1)} (${student.letterGrade})`);
```

```swift !! swift
// Swift 解答
struct Student {
    let name: String
    let id: String
    private var grades: [Double] = []
    
    init(name: String, id: String) {
        self.name = name
        self.id = id
    }
    
    mutating func addGrade(_ grade: Double) {
        if grade >= 0 && grade <= 100 {
            grades.append(grade)
        }
    }
    
    var average: Double {
        if grades.isEmpty { return 0 }
        return grades.reduce(0, +) / Double(grades.count)
    }
    
    var letterGrade: String {
        let avg = average
        switch avg {
        case 90...:
            return "A"
        case 80..<90:
            return "B"
        case 70..<80:
            return "C"
        case 60..<70:
            return "D"
        default:
            return "F"
        }
    }
}

var student = Student(name: "Alice", id: "12345")
student.addGrade(85)
student.addGrade(92)
student.addGrade(78)
print("\(student.name): \(String(format: "%.1f", student.average)) (\(student.letterGrade))")
```
</UniversalEditor>

### 練習 2：實現形狀層次結構

<UniversalEditor title="練習 2：形狀層次結構" compare={true}>
```javascript !! js
// JavaScript 解答
class Shape {
    constructor(color) {
        this.color = color;
    }
    
    area() {
        throw new Error("area() 必須被實現");
    }
    
    describe() {
        console.log(`一個 ${this.color} 顏色的形狀，面積為 ${this.area()}`);
    }
}

class Circle extends Shape {
    constructor(color, radius) {
        super(color);
        this.radius = radius;
    }
    
    area() {
        return Math.PI * this.radius * this.radius;
    }
}

class Rectangle extends Shape {
    constructor(color, width, height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    area() {
        return this.width * this.height;
    }
}

const shapes = [
    new Circle("紅色", 5),
    new Rectangle("藍色", 4, 6)
];

shapes.forEach(shape => shape.describe());
```

```swift !! swift
// Swift 解答
class Shape {
    var color: String
    
    init(color: String) {
        self.color = color
    }
    
    func area() -> Double {
        fatalError("area() 必須被實現")
    }
    
    func describe() {
        print("一個 \(color) 顏色的形狀，面積為 \(area())")
    }
}

class Circle: Shape {
    var radius: Double
    
    init(color: String, radius: Double) {
        self.radius = radius
        super.init(color: color)
    }
    
    override func area() -> Double {
        return Double.pi * radius * radius
    }
}

class Rectangle: Shape {
    var width: Double
    var height: Double
    
    init(color: String, width: Double, height: Double) {
        self.width = width
        self.height = height
        super.init(color: color)
    }
    
    override func area() -> Double {
        return width * height
    }
}

let shapes: [Shape] = [
    Circle(color: "紅色", radius: 5),
    Rectangle(color: "藍色", width: 4, height: 6)
]

for shape in shapes {
    shape.describe()
}
```
</UniversalEditor>

## 關鍵要點

### Swift OOP 優勢
1. **值類型**：結構體提供值語義，防止意外突變
2. **類型安全**：強類型防止執行時錯誤
3. **協議**：類和結構體的靈活介面系統
4. **屬性觀察器**：內建的屬性變化通知支援
5. **記憶體管理**：ARC 自動處理記憶體

### 與 JavaScript 的關鍵差異
1. **值 vs 參考**：Swift 區分值類型和參考類型
2. **繼承**：只有 Swift 類支援繼承
3. **可變性**：Swift 結構體需要顯式的可變方法
4. **協議**：Swift 有正式的協議系統 vs JavaScript 鴨子類型
5. **記憶體管理**：ARC vs 垃圾回收

### 最佳實踐
1. **對簡單資料模型使用結構體**和值語義
2. **對需要繼承的複雜物件使用類**
3. **利用協議**實現靈活介面
4. **盡可能優先使用組合而非繼承**
5. **使用屬性觀察器**進行響應式程式設計
6. **考慮值類型**的執行緒安全性和效能

### 下一步
在下一個模組中，我們將探索 Swift 中的協議和擴展，包括協議導向程式設計、協議擴展，以及它們與 JavaScript 介面和混入方法的對比。 